package ar.edu.unq.leezsi.html;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.servlet.http.HttpServletResponse;

import ar.edu.unq.leezsi.continuation.ContinuationContext;
import ar.edu.unq.leezsi.core.Context;
import ar.edu.unq.leezsi.core.CowebApplication;
import ar.edu.unq.leezsi.model.Model;
import ar.edu.unq.leezsi.requestcycle.HTTPAction;
import ar.edu.unq.leezsi.requestcycle.RequestCycle;
import ar.edu.unq.leezsi.utils.ClassUtils;
import ar.edu.unq.leezsi.utils.ResponseWriter;

public abstract class Component<T> {
	private final CowebApplication application = CowebApplication.get();
	private Context context;
	private Component<?> parent;
	private final Map<String, List<String>> properties = new HashMap<String, List<String>>();
	protected final List<Component<?>> children = new ArrayList<Component<?>>();
	private Model<T> model;

	public Component() {
		this.application.applySecurityInstantiationPolicy(this);
		this.application.inject(this);
	}

	public void add(Component<?>... components) {
		for (Component<?> component : components) {
			this.getChildren().add(component);
			component.setParent(this);
		}

	}

	public Component<T> appendAttribute(String attribute, String value) {
		if (this.properties.containsKey(attribute)) {
			this.properties.get(attribute).add(value);
		} else {
			this.setAttribute(attribute, value);
		}
		return this;
	}

	protected String attributes() {
		StringBuilder atts = new StringBuilder();
		for (String property : this.properties.keySet()) {
			List<String> values = this.properties.get(property);
			atts.append(property);
			for (String value : values) {
				atts.append("=\"").append(value).append(" ");
			}
			atts.append("\"");
		}
		return atts.toString();
	}

	protected void configureResponse(ResponseWriter responseWriter) {

	}

	protected void doOnBeforeRenderer() {
		this.application.applySecurityPreOnBeforeRendererPolicy(this);
		this.onBeforeRenderer();
	}

	protected void executeAction(HTTPAction httpAction) {
		// for subclasss

	}

	protected CowebApplication getApplication() {
		return this.application;
	}

	public List<Component<?>> getChildren() {
		return this.children;
	}

	protected Context getContext() {
		if ((this.context == null) && (this.getParent() != null)) {
			return this.getParent().getContext();
		}
		return this.context;
	}

	public final ContinuationContext getContinuationContext() {
		return this.getContext().getContinuationContext();
	}

	public Model<T> getModel() {

		return this.model == null ? new Model<T>("") : this.model;
	}

	protected final Component<?> getParent() {
		return this.parent;
	}

	protected final String getProperty(String property) {
		try {
			Properties properties = ClassUtils.wrapperFor(this).getProperties();
			return properties.getProperty(property);
		} catch (Exception e) {
			return this.getParent().getProperty(property);
		}
	}

	protected final String getProperty(String property, String defaultValue) {
		try {
			Properties properties = ClassUtils.wrapperFor(this).getProperties();
			return properties.getProperty(property, defaultValue);
		} catch (Exception e) {
			return this.getParent().getProperty(property, defaultValue);
		}
	}

	protected void internalRender() {

	}

	protected void onAfterRenderer() {

	}

	protected void onBeforeRenderer() {

	}

	protected void onSetParent(Component<?> parent2) {

	}

	protected abstract void renderOn(final HttpServletResponse response);

	public final RequestCycle requestCycle() {
		return this.getContext().getRequestCycle();
	}

	public void setAttribute(String attribute, String value) {
		List<String> values = new ArrayList<String>();
		values.add(value);
		this.properties.put(attribute, values);
	}

	protected final void setContext(Context context) {
		this.context = context;
	}

	public void setModel(Model<T> model) {
		this.model = model;
	}

	protected final void setParent(Component<?> parent) {
		this.parent = parent;
		this.onSetParent(parent);
	}

	protected abstract String tag();
}
